#include "StreamReader.h"


namespace bl{

const uint32 StreamReader::_BufferSize = 64*1024;	// 64k

StreamReader::StreamReader(void) : file_buffer_size_(StreamReader::_BufferSize), file_buffer_count_(0), current_pos_(0), last_is_return_carriage_(false)
{
	file_buffer_.reset(new uint8[GetFileBufferSize()]);
}

StreamReader::~StreamReader(void)
{
}

bool StreamReader::Init(const tchar* fileName)
{
	if(!file_.OpenRead(fileName))
		return false;

	// Check BOM
	const uint32 header_length = 3;
	uint8 buf[header_length];
	uint32 bytes_read = 0;
	if(!file_.Read(buf, header_length, &bytes_read) )
		return false;

	const uint8 utf8[3] = {0xEF, 0xBB, 0xBF};
	const uint8 unicode16_be[2] = {0xFE, 0xFE};
	const uint8 unicode16_le[2] = {0xFF, 0xFE};

	bool is_unicode = false;

	encoding_ = AsciiEncoding;

	if(bytes_read >= 2)
	{
		if(0 == memcmp(unicode16_be, buf, 2))
		{
			encoding_ = Unicode16BEEncoding;
			is_unicode = true;
			file_.Seek(2);
		}
		else if(0 == memcmp(unicode16_le, buf, 2))
		{
			encoding_ = Unicode16LEEncoding;
			is_unicode = true;
			file_.Seek(2);
		}
		else
		{
			if(bytes_read == 3)
			{
				if(0 == memcmp(utf8, buf, 3))
				{
					encoding_ = UTF8Encoding;
					is_unicode = true;
				}
			}
		}
	}

	if(!is_unicode)
		file_.Seek(0);

	return true;
}

StreamReader::Encoding StreamReader::ContentEncoding() const
{
	return encoding_;
}

bool StreamReader::ReadLineAnsi(std::string& line)
{
	bool has_got_content = false;
	uint32 old_pos = current_pos_;
	while(true)
	{
		if(current_pos_ >= file_buffer_count_) // End to the buffer, we need to read from file
		{
			if(old_pos < current_pos_) // Copy the left buffer to string
			{
				line.append( (const char*)(file_buffer_.get()+old_pos), (current_pos_-old_pos) );
				has_got_content = true;
			}

			current_pos_ = 0;
			old_pos = current_pos_;
			if(!file_.Read(file_buffer_.get(), GetFileBufferSize(), &file_buffer_count_ ))
			{
				// error
				file_buffer_count_  = 0;
			}
		}

		if(file_buffer_count_ == 0)	// We have read to the end of file
			break;

		char c = file_buffer_[current_pos_];
		if( c == '\r' || c=='\n' )
		{
			bool tmp_last_is_return_carriage = last_is_return_carriage_;
			if(c== '\r')
				last_is_return_carriage_ = true;
			else
				last_is_return_carriage_ = false;

			if(current_pos_>old_pos)
			{
				line.append( (const char*)(file_buffer_.get()+old_pos), (current_pos_-old_pos) );
				has_got_content = true;
				++current_pos_;
				break;
			}
			else
			{
				if( tmp_last_is_return_carriage && (c=='\n') )
				{
					++current_pos_;
					old_pos = current_pos_;
					continue;
				}
				else
				{
					//if(has_got_content)
					//	break;
					++current_pos_;
					old_pos = current_pos_;
					break;
				}
			}
		}
		else
		{
			++current_pos_;
		}

	}

	// Reach the end of file
	if(file_buffer_count_==0 && !has_got_content)
		return false;

	return true;
}

bool StreamReader::ReadLine(std::wstring& line)
{
	bool has_got_content = false;
	uint32 old_pos = current_pos_;
	while(true)
	{
		if(current_pos_*sizeof(char16) >= file_buffer_count_) // End to the buffer, we need to read from file
		{
			if(old_pos < current_pos_) // Copy the left buffer to string
			{
				line.append( (const char16*)(reinterpret_cast<char16*>(file_buffer_.get())+old_pos), (current_pos_-old_pos) );
				has_got_content = true;
			}

			current_pos_ = 0;
			old_pos = current_pos_;
			if(!file_.Read(file_buffer_.get(), GetFileBufferSize(), &file_buffer_count_ ))
			{
				// error
				file_buffer_count_  = 0;
			}
		}

		if(file_buffer_count_ == 0)	// We have read to the end of file
			break;

		char16 c = *(reinterpret_cast<char16*>(file_buffer_.get()) + current_pos_);
		if( c == L'\r' || c==L'\n' )
		{
			bool tmp_last_is_return_carriage = last_is_return_carriage_;
			if(c== '\r')
				last_is_return_carriage_ = true;
			else
				last_is_return_carriage_ = false;

			if(current_pos_>old_pos)
			{
				line.append( (const char16*)(reinterpret_cast<char16*>(file_buffer_.get())+old_pos), (current_pos_-old_pos) );
				has_got_content = true;
				++current_pos_;
				break;
			}
			else
			{
				if( tmp_last_is_return_carriage && (c==L'\n') )
				{
					++current_pos_;
					old_pos = current_pos_;
					continue;
				}
				else
				{
					++current_pos_;
					old_pos = current_pos_;
					break;
				}
			}
		}
		else
		{
			++current_pos_;
		}

	}

	// Reach the end of file and we didn't get any content. then signal end of file
	if(file_buffer_count_==0 && !has_got_content)
		return false;

	return true;
}


} // End of BL